﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using DiscoveryLogic.Analytics.Diagrams.Physics.Renderers;

namespace DiscoveryLogic.Analytics.Diagrams.Physics
{
	public class Spring
	{
		#region props
		private ISpringRenderer _Renderer;
		/// <summary></summary>
		public ISpringRenderer Renderer
		{
			get
			{
				return this._Renderer;
			}
			set
			{
				this._Renderer = value;
			}
		}

		private Particle _From;
		/// <summary></summary>
		public Particle From
		{
			get
			{
				return this._From;
			}
			set
			{
				this._From = value;
			}
		}

		private Particle _To;
		/// <summary></summary>
		public Particle To
		{
			get
			{
				return this._To;
			}
			set
			{
				this._To = value;
			}
		}

		private double _RestLength;
		/// <summary></summary>
		public double RestLength
		{
			get
			{
				return this._RestLength;
			}
			set
			{
				this._RestLength = value;
			}
		}

		private double _SpringConst;
		/// <summary></summary>
		public double SpringConst
		{
			get
			{
				return this._SpringConst;
			}
			set
			{
				this._SpringConst = value;
			}
		}

		private double _DampingConstant;
		/// <summary></summary>
		public double DampingConstant
		{
			get
			{
				return this._DampingConstant;
			}
			set
			{
				this._DampingConstant = value;
			}
		}

		private Pen _SpringPen;
		/// <summary></summary>
		public Pen SpringPen
		{
			get
			{
				return this._SpringPen;
			}
			set
			{
				this._SpringPen = value;
			}
		}
		#endregion

		#region ctor
		public Spring(Particle from, Particle to, double restLen, 
			double springConst, double dampingConst)
		{
			this._From = from;
			this._To = to;
			this._RestLength = restLen;
			this._SpringConst = springConst;
			this._DampingConstant = dampingConst;
			this._SpringPen=new Pen(Brushes.White, 4.0f);
			this._Renderer = null;
		}
		#endregion

		#region pub methods
		public void Apply()
		{
            Vector deltaX = From.Position - To.Position;
            Vector deltaV = From.Velocity - To.Velocity;

            double term1 = this._SpringConst * (deltaX.Length - RestLength);
		    double term2 = DampingConstant*Vector.AngleBetween(deltaV, deltaX)/Math.Max(1, deltaX.Length);

            double leftMultiplicant = -(term1 + term2);
            Vector force = deltaX;

            // FIXME: Should do something about zero-length springs here as the
            // simulation will brake on zero-length springs...
		    force *= 1.0f/Math.Max(1, deltaX.Length);
            force *= leftMultiplicant;

            From.Force += force;
            To.Force -= force;
		}

		public void Render(DrawingContext dc)
		{
			if(this._Renderer !=null)
				this._Renderer.Render(this, dc);
		}
		#endregion
	}
}
